package com.allen.app.common.config.rabbitmq;


import cn.hutool.core.util.RandomUtil;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;

@Slf4j
public class DefaultRabbitInitializer implements ApplicationContextAware, RabbitInitializer {

    private static final String BEAN_NAME_SUFFIX = ".bean";
    private static final int BINDING_SUFFIX_LENGTH = 6;

    private DefaultListableBeanFactory beanFactory;


    @Override
    public void delayDifferentTime(String exchange, String dlx) {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        CustomExchange directExchange =this.registerBean(exchange, () -> new CustomExchange(exchange, "x-delayed-message", DURABLE, AUTO_DELETE, args));

        Queue dlxQueue = this.registerBean(dlx, () -> new Queue(dlx, DURABLE, EXCLUSIVE, AUTO_DELETE));
        this.beanFactory.registerSingleton(dlx + ".binding-" + RandomUtil.randomInt(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(dlxQueue).to(directExchange).with(dlx).noargs());

        /*
        Queue dlxQueue = this.registerBean(dlx, () -> new Queue(dlx, DURABLE, EXCLUSIVE, AUTO_DELETE));
        this.beanFactory.registerSingleton(dlx + ".binding-" + Random.random(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(dlxQueue).to(directExchange).with(to).noargs());
        Queue toQueue = this.registerBean(to, () -> new Queue(to, DURABLE, EXCLUSIVE, AUTO_DELETE));
        this.beanFactory.registerSingleton(to + ".binding-" + Random.random(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(toQueue).to(directExchange).with(dlx).noargs());*/
    }


    @Override
    public void delay(String exchange, String dlx, String to) {
        DirectExchange directExchange = this.registerBean(exchange, () -> new DirectExchange(exchange, DURABLE, AUTO_DELETE));
        Queue dlxQueue = this.registerBean(dlx, () ->
            new Queue(dlx, DURABLE, EXCLUSIVE, AUTO_DELETE, ImmutableMap
                .of("x-dead-letter-exchange", exchange, "x-dead-letter-routing-key", to))
        );
        this.beanFactory.registerSingleton(dlx + ".binding-" + RandomUtil.randomInt(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(dlxQueue).to(directExchange).with(dlx));
        Queue toQueue = this.registerBean(to, () -> new Queue(to, DURABLE, EXCLUSIVE, AUTO_DELETE));
        this.beanFactory.registerSingleton(to + ".binding-" + RandomUtil.randomInt(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(toQueue).to(directExchange).with(to));
    }

    @Override
    public void normal(String exchange, String queue) {
        DirectExchange directExchange = this.registerBean(exchange, () -> new DirectExchange(exchange, DURABLE, AUTO_DELETE));
        Queue instance = this.registerBean(queue, () -> new Queue(queue, DURABLE, EXCLUSIVE, AUTO_DELETE));
        this.beanFactory.registerSingleton(queue + ".binding-" + RandomUtil.randomInt(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(instance).to(directExchange).with(queue));
    }

    @Override
    public void publish(String exchange, String... queues) {
        FanoutExchange fanoutExchange = this.registerBean(exchange, () -> new FanoutExchange(exchange, DURABLE, AUTO_DELETE));
        Stream.of(queues).forEach(name -> {
            Queue queue = this.registerBean(name, () -> new Queue(name, DURABLE, EXCLUSIVE, AUTO_DELETE));
            this.beanFactory.registerSingleton(queue + ".binding-" + RandomUtil.randomInt(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(queue).to(fanoutExchange));
        });
    }

    @Override
    public void topic(String exchange, String queue, String routingKey) {
        TopicExchange topicExchange = this.registerBean(exchange, () -> new TopicExchange(exchange, DURABLE, AUTO_DELETE));
        Queue instance = this.registerBean(queue, () -> new Queue(queue, DURABLE, EXCLUSIVE, AUTO_DELETE));
        this.beanFactory.registerSingleton(queue + ".binding-" + RandomUtil.randomInt(BINDING_SUFFIX_LENGTH), BindingBuilder.bind(instance).to(topicExchange).with(routingKey));
    }

    @SuppressWarnings("unchecked")
    private <T> T registerBean(String beanName, Supplier<T> supplier) {
        String name = beanName + BEAN_NAME_SUFFIX;
        if (beanFactory.containsBean(name)) {
            return (T) beanFactory.getBean(name);
        } else {
            T bean = supplier.get();
            beanFactory.registerSingleton(name, bean);
            log.info("\t===>>\t\t[RabbitInitializer] bean '{}' registered", beanName);
            return bean;
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
    }
}
